﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lesson10
{
    class Test
    {
        public int i = 1;
        public Test Clone()
        {
            // 浅拷贝
            //return (Test)this.MemberwiseClone();
            return MemberwiseClone() as Test;
        }

        // 虚方法用来重写的
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override bool Equals(object obj)
        {
            return base.Equals(obj);
        }

        public override string ToString()
        {
            return base.ToString();
        }
    }
    internal class Program
    {
        static void Main(string[] args)
        {
            #region 万物之父中的方法
            // object中的方法

            // object中的静态方法
            // 静态方法Equals 判断两个对象是否相等  public static bool Equals(object objA, object objB)
            // 最终判断权，交给左侧对象的Equals方法
            // 不管值类型、引用类型都会按照左侧对象的Equals方法的规则来进行比较
            Console.WriteLine(Object.Equals(1, 1)); // 用Object和object是一样的
            Test test = new Test();
            Test test2 = new Test();
            Console.WriteLine(Object.Equals(test, test2));

            // 静态方法ReferenceEquals   public static bool ReferenceEquals(object objA, object objB)
            // 比较两个对象是否是相同的引用，主要用来比较引用类型的对象
            // 值类型对象返回值始终是false
            Console.WriteLine(Object.ReferenceEquals(test, test2)); // false

            // object中的成员方法
            // 普通方法GetType         public extern Type GetType();
            //  该方法在反射中是非常重要的，主要作用就是获取对象运行时的类型，通过Type结合反射可以做很多关于对象的操作
            Type type = test.GetType();
            Console.WriteLine(type);

            // 普通方法MemberwiseClone  该方法用protected修饰  protected extern object MemberwiseClone();
            //  该方法用于获取对象的浅拷贝对象， 意思是拷贝了引用地址
            Test test3 = test.Clone();      // MemberwiseClone是protected修饰的，需要再子类中覆写一下
            Console.WriteLine(test3.i);


            // object中的虚方法
            // 虚方法Equals    public virtual bool Equals(object obj)
            // 该方法用于判断两个对象是否相等，默认是比较引用地址，即相当于ReferenceEquals
            // 但c#官方在所有值类型的基类 System.ValueType中重写了Equals方法， 用来比较值相等
            // 我们也可以重写该方法，定义自己的比较相等的规则


            // 虚方法GetHashCode  public virtual int GetHashCode()
            // 该方法用于获取对象的哈希值
            // (是一种算法算出的，表示对象的唯一编码，不同对象哈希码有可能一样，具体值根据哈希算法决定)
            // 很少用

            // 虚方法ToString   public virtual string ToString()
            // 该方法用于返回当前对象代表的字符串，我们可以重写它定义我们自己的对象转换成字符串的规则
            // 很常用

            #endregion

            #region String
            // String中的方法
            String str = "hello world";
            // 字符串指定位置的获取  得到的是字符， 字符串本质是char数组
            Console.WriteLine(str[0]);  // 这是因为String类有索引器

            // 转为char数组
            char[] chars = str.ToCharArray();
            Console.WriteLine(chars[0]);
            // 字符串有Length属性
            for (int i = 0; i < str.Length; i++)
            {
                Console.WriteLine(str[i]);
            }

            // 字符串拼接
            str = String.Format("{0}{1}", 1, 222);  // 使用了静态方法 Format
            Console.WriteLine(str);

            // 正向查找字符位置
            str = "你好，世界";
            int index = str.IndexOf("世"); // 返回字符的索引位置
            Console.WriteLine(index);
            int index2 = str.IndexOf("中"); // 没找到返回-1
            Console.WriteLine(index2);

            // 反向查找指定字符串位置
            str = "我是China唐";
            int index3 = str.LastIndexOf("唐"); // 返回字符的索引位置
            Console.WriteLine(index3);          // 7 返回最后一个唐的索引位置，还是从前面开始数的
            int index4 = str.LastIndexOf("宋"); // 没找到返回-1

            // 移除指定位置后的字符
            str = "hello unity";
            str = str.Remove(5); // 从第5个位置开始移除 会返回新的字符串，不会改变原字符串
            Console.WriteLine(str); // hello

            str = str.Remove(1, 2); // 第一个参数是开始位置，第二个参数是移除的长度（字符个数）

            // 替换指定字符串
            str = "我是哈利卡哈啦啦";
            str = str.Replace("哈", "哈利"); // 替换所有的哈
            Console.WriteLine(str);

            // 大小写转换
            str = "hello world";
            str = str.ToUpper(); // 转为大写
            Console.WriteLine(str);
            str = str.ToLower(); // 转为小写
            Console.WriteLine(str);

            // 字符串截取
            str = "hello world";
            str = str.Substring(6); // 从第6个位置开始截取到最后
            Console.WriteLine(str); // world
            str = str.Substring(0, 5); // 第一个参数是开始位置，第二个参数是截取的长度（字符个数） 这个个数不能超过字符串长度
            Console.WriteLine(str); // world

            // 字符串切割
            str = "1,2,3,4,5,6,7,8";
            string[] strArray = str.Split(','); // 以逗号为分隔符切割字符串
            foreach (var item in strArray)
            {
                Console.WriteLine(item);
            }

            #endregion

            #region StringBuilder
            // StringBuilder是c#提供的一个用于处理字符串的公共类
            // 主要解决问题是 修改字符串而不创建新的对象，需要频繁修改和拼接的字符串可以使用，提升性能
            // 使用前需要引用命名空间 System.Text

            // 直接指明内容
            StringBuilder sb = new StringBuilder("hello world");
            Console.WriteLine(sb);

            // 容量
            // StringBuilder的容量是指可以容纳的字符个数，默认是16个字符，每次增加时，会自动扩容
            Console.WriteLine(sb.Capacity); // 16
            StringBuilder sb2 = new StringBuilder(100); // 指定容量为100

            // 获得字符长度
            Console.WriteLine(sb.Length);   // 这个是实际字符串的长度

            // StringBuilder的增删改查
            // 增
            sb.Append("hello"); // 在末尾追加字符串
            sb.AppendLine("hello"); // 在末尾追加字符串并换行
            sb.AppendFormat("{0}{1}", 1, 2); // 格式化字符串

            Console.WriteLine(sb);

            // 插入
            sb.Insert(0, "hello"); // 在指定位置插入字符串
            Console.WriteLine(sb);

            // 删除
            sb.Remove(0, 5); // 删除指定位置的字符串 第一个是开始位置，第二个是删除的长度
            Console.WriteLine(sb);

            // 清空
            //sb.Clear(); // 清空字符串
            //Console.WriteLine(sb); // 只会清空内容，不会释放内存

            // 查
            Console.WriteLine(sb[0]);  // 查询其中一个字符

            // 改
            sb[0] = 'h'; // 修改指定位置的字符, 只有StringBuilder可以修改, String不可以
            Console.WriteLine(sb[0]);

            // 替换
            sb.Replace("h", "H"); // 替换指定的字符
            Console.WriteLine(sb);

            // 重新赋值
            // 先清空再添加
            //sb.Clear();
            //sb.Append("hello world");
            
            // 判断相等
            if(sb.Equals("hello world"))
            {
                Console.WriteLine("相等");
            }
            else
            {
                Console.WriteLine("不相等");
            }


            #endregion

            #region 结构体和类的区别
            // 结构体和类最大的区别是在存储空间上，因为结构体是值类型，类是引用类型
            // 结构体在栈中分配内存，类在堆中分配内存

            // 结构体和类在使用上很相似，结构体甚至可以用面向对象的思想来形容一类对象
            // 所以结构体具备面向对象中封装的特性，但不具备继承和多态的特性，所以使用率很少
            // 由于结构体不具备继承特性，所以不能使用 protected 修饰符

            // 细节区别
            // 1. 结构体是值类型，类是引用类型
            // 2. 结构体在栈中分配内存，类在堆中分配内存
            // 3. 结构体成员不能使用protected访问修饰符，而类可以
            // 4. 结构体成员变量声明不能指定初始值，而类可以
            // 5. 结构体不能声明无参的构造函数，而类可以
            // 6. 结构体声明有参构造函数后，无参构造不会被顶掉
            // 7. 结构体不能声明析构函数，而类可以
            // 8. 结构体不能被继承，而类可以
            // 9. 结构体需要再构造函数中初始化所有成员变量，而类随意
            // 10. 结构体不能被静态static修饰(不存在静态结构体)，而类可以
            // 11. 结构体不能在自己内部声明和自己一样的结构体变量，而类可以


            // 结构体的特别之处
            // 结构体可以继承 接口 因为接口是行为的抽象

            // 如何选择结构体和类
            // 1. 想要继承和多态时，直接淘汰结构体，比如 玩家、怪物、道具等
            // 2. 对象是数据集合时，优先考虑结构体，比如 颜色、坐标、时间等
            // 3. 从值类型和引用类型赋值时的区别考虑，比如经常被赋值传递的对象，并且改变赋值对象，原对象不想跟着变化时，就用结构体。比如坐标、向量、旋转等
            #endregion

            #region 抽象类和接口的区别
            // 抽象类和抽象方法
            // abstract修饰的类和方法
            // 抽象类 不能实例化
            // 抽象方法只能在抽象类中声明 是个纯虚方法 必须在子类中重写

            // 接口
            // interface 自定义类型
            // 是行为的抽象
            // 不包含成员变量
            // 仅包含方法、属性、索引器、事件，成员都不能实现，建议不写访问修饰符，默认public

            // 相同点
            // 1、都可以被继承
            // 2、都不能实例化
            // 3、都可以包含方法声明
            // 4、子类必须实现未实现的方法
            // 5、都遵循里氏替换原则

            // 区别
            // 1、抽象类中可以有构造函数，接口中没有
            // 2、抽象类只能被单一继承，接口可以被继承多个
            // 3、抽象类可以包含成员变量，接口中没有
            // 4、抽象类可以声明成员方法，虚方法，抽象方法，静态方法，接口中只能声明没有实现的抽象方法
            // 5、抽象类可以包含访问修饰符，接口中不建议写，默认public

            // 如何选择抽象类 和 接口
            // 表示对象的用抽象类，表示行为拓展的用接口
            // 不同对象拥有的共同行为，往往用接口实现

            #endregion
        }
    }
}
